package cn.com.netedge.iot.common.utils;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import javax.annotation.PostConstruct;

import cn.com.netedge.iot.common.constants.CacheKeyConstants;
import cn.com.netedge.iot.common.constants.Globals;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import com.alibaba.fastjson2.JSON;

@Component
public class RedisUtil {
    private static final Logger logger = LoggerFactory.getLogger(RedisUtil.class);

    private static StringRedisTemplate defaultRedisTemplate;
    private static RedisTemplate<String, byte[]> staticBytesRedisTemplate;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private RedisTemplate<String, byte[]> bytesRedisTemplate;

    @PostConstruct
    public void init() {
        defaultRedisTemplate = stringRedisTemplate;
        staticBytesRedisTemplate = bytesRedisTemplate;
    }

    public static void set(String key, String value) {
        set(key, value, Globals.DEFAULT_CACHE_EXPIRE_TIME, TimeUnit.SECONDS);
    }

    public static void setPermanently(String key, String value) {
        defaultRedisTemplate.opsForValue().set(key, value);
    }

    public static void set(String key, String value, long timeout, TimeUnit unit) {
        defaultRedisTemplate.opsForValue().set(key, value, timeout, unit);
    }

    /**
     * 获取缓存
     *
     * @param key 键
     * @return 值
     */
    public static String get(String key) {
        return defaultRedisTemplate.opsForValue().get(key);
    }

    /**
     * 删除缓存
     *
     * @param key 键
     */
    public static void delete(String key) {
        defaultRedisTemplate.delete(key);
    }

    /**
     * 设置缓存，如果不存在
     *
     * @param key     键
     * @param value   值
     * @param timeout 超时时间
     * @param unit    超时单位
     * @return 如果设置成功则返回 true
     */
    public static boolean setIfAbsent(String key, String value, long timeout, TimeUnit unit) {
        return defaultRedisTemplate.opsForValue().setIfAbsent(key, value, timeout, unit);
    }

    /**
     * 判断是否存在键
     *
     * @param key 键
     * @return true or false
     */
    public static boolean hasKey(String key) {
        return Boolean.TRUE.equals(defaultRedisTemplate.hasKey(key));
    }

    public static long llen(String key) {
        return defaultRedisTemplate.opsForList().size(key);
    }

    public static long incr(String key) {
        return defaultRedisTemplate.opsForValue().increment(key);
    }

    public static Set<String> hkeys(String key) {
        HashOperations<String, String, String> hashOps = defaultRedisTemplate.opsForHash();
        return hashOps.keys(key);
    }

    public static String hget(String key, String field) {
        HashOperations<String, String, String> hashOps = defaultRedisTemplate.opsForHash();
        return hashOps.get(key, field);
    }

    public static void hset(String key, String field, String value) {
        HashOperations<String, String, String> hashOps = defaultRedisTemplate.opsForHash();
        hashOps.put(key, field, value);
    }

    public static Long hdel(String key, String field) {
        HashOperations<String, String, String> hashOps = defaultRedisTemplate.opsForHash();
        return hashOps.delete(key, field);
    }

    public static void rightPush(String key, String value) {
        defaultRedisTemplate.opsForList().rightPush(key, value);
    }

    public static void rightPush(String key, Object value) {
        byte[] bytes = ProtostuffUtil.serialize(value);
        staticBytesRedisTemplate.opsForList().rightPush(key, bytes);
    }

    public static List<String> range(String key, long start, long end) {
        ListOperations<String, String> listOps = defaultRedisTemplate.opsForList();
        return listOps.range(key, start, end);
    }

    public static List range(String key, long start, long end, Class<?> clazz) {
        ListOperations<String, byte[]> listOps = staticBytesRedisTemplate.opsForList();
        List<byte[]> list = listOps.range(key, start, end);
        if (list == null || list.isEmpty()) {
            return null;
        }
        List result = new ArrayList<>();
        for (byte[] item : list) {
            result.add(ProtostuffUtil.deserialize(item, clazz));
        }
        return result;
    }

    public static void trim(String key, long start, long end) {
        defaultRedisTemplate.opsForList().trim(key, start, end);
    }

    public static String rightPop(String key) {
        ListOperations<String, String> listOps = defaultRedisTemplate.opsForList();
        return listOps.rightPop(key);
    }

    public static <T> T rightPop(String key, Class<T> clazz, long timeout) {
        ListOperations<String, byte[]> listOps = staticBytesRedisTemplate.opsForList();
        byte[] bytes = listOps.rightPop(key, timeout, TimeUnit.SECONDS);
        if (bytes == null || bytes.length == 0) {
            return null;
        }
        return ProtostuffUtil.deserialize(bytes, clazz);
    }

    public static String leftPop(String key) {
        ListOperations<String, String> listOps = defaultRedisTemplate.opsForList();
        return listOps.leftPop(key);
    }

    public static <T> T leftPop(String key, Class<T> clazz, long timeout) {
        ListOperations<String, byte[]> listOps = staticBytesRedisTemplate.opsForList();
        byte[] bytes = listOps.leftPop(key, timeout, TimeUnit.SECONDS);
        if (bytes == null || bytes.length == 0) {
            return null;
        }
        return ProtostuffUtil.deserialize(bytes, clazz);
    }

    public static void expire(String key, int timeout) {
        defaultRedisTemplate.expire(key, timeout, TimeUnit.SECONDS);
    }

    public static void setCacheObject(String key, Object value) {
        set(key, JSON.toJSONString(value));
    }

    public static <T> T getCacheObject(String key, Class<T> clazz) {
        String value = get(key);
        if (StringUtil.isEmpty(value)) {
            return null;
        }
        return JSON.parseObject(value, clazz);
    }

    public static void setCacheList(String key, List dataList) {
        if (dataList.isEmpty() || dataList.size() == 0) {
            logger.error("key {}, data list empty！", key);
            return;
        }
        set(key, JSON.toJSONString(dataList));
    }

    public static List getCacheList(String key, Class<?> clazz) {
        String value = get(key);
        if (StringUtil.isEmpty(value)) {
            return null;
        }
        return JSON.parseArray(value, clazz);
    }

    public static <T> void enqueue(String deviceNumber, T value, int priority) {
        if (value == null) {
            return;
        }
        String key = CacheKeyConstants.KEY_DEVICE_QUEUE + deviceNumber;
        defaultRedisTemplate.opsForZSet().add(key, JSON.toJSONString(value), priority);
    }

    public static <T> T dequeue(String deviceNumber, Class<T> clazz) {
        String key = CacheKeyConstants.KEY_DEVICE_QUEUE + deviceNumber;
        Set<String> tasks = defaultRedisTemplate.opsForZSet().range(key, 0, 0);
        if (tasks != null && !tasks.isEmpty()) {
            String task = tasks.iterator().next();
            // 从队列中删除该任务
            defaultRedisTemplate.opsForZSet().remove(key, task);
            return JSON.parseObject(task, clazz);
        }
        return null;
    }

    public static void publish(String topic, String message) {
        defaultRedisTemplate.convertAndSend(topic, message);
    }

}
